home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ShareWare OnLine 2
/
ShareWare OnLine Volume 2 (CMS Software)(1993).iso
/
util2
/
zdir18.zip
/
ZDIR18.ASM
< prev
next >
Wrap
Assembly Source File
|
1993-01-27
|
47KB
|
1,898 lines
;ZDIR .ZIP directory utility
;From a disassembly and hack of ADIR.EXE
;v1.8 Update for PKZIP v2.04x
; - Added new "deflate" method.
; - Moved versions out to HISTORY.TXT
; - Removed earlier "v1.x" comments
FALSE equ 0
TRUE equ NOT FALSE
PLAIN equ 0
VERBO equ 0FFh
MONOSYL equ 1
STRINGWILD Equ '*' ; wildcard for strings
CHARWILD Equ '?' ; wildcard for single chars
CR equ 0DH
LF equ 0AH
STDOUT equ 1
STDERR equ 2
ENDOFS equ 4096 ;enough for largest possible
;ZIP end directory structure.
;(to include XMODEM padding)
;(and hormongous comments!)
;(was 256 in v1.3)
Print macro name ; display a field
mov dx,offset name
call PrintS
endm
;PKZIP central directory structure:
zdirEntry STRUC
zsig1 db 50H,4BH,01H,02H ;central file header signature 4 bytes
;(0x02014b50)
zVerMade dw ? ;version made by 2 bytes
zVerExt dw ? ;version needed to extract 2 bytes
zBitFlag dw ? ;general purpose bit flag 2 bytes
zCmpMeth dw ? ;compression method 2 bytes
zModTime dw ? ;last mod file time 2 bytes
zModDate dw ? ;last mod file date 2 bytes
zCrc32 dw ?,? ;crc-32 4 bytes
zCmpSiz dw ?,? ;compressed size 4 bytes
zUncmpSiz dw ?,? ;uncompressed size 4 bytes
zNameLen dw ? ;filename length 2 bytes
zExtraLen dw ? ;extra field length 2 bytes
zFilCmtLen dw ? ;file comment length 2 bytes
zDskNrPtr dw ? ;disk number start 2 bytes
zIntAttr dw ? ;internal file attributes 2 bytes
zExtAttr dw ?,? ;external file attributes 4 bytes
zHdrOfs dw ?,? ;relative offset of local header 4 bytes
zFilename db ? ;filename (variable size)
;extra field (variable size)
;file comment (variable size)
zdirEntry ENDS
; End of central dir record:
zdirEnd STRUC
zEndSig db 50H,4BH,05H,06H ;end of central dir signature 4 bytes
;(0x06054b50)
zDskNr dw ? ;number of this disk 2 bytes
zDirDsk dw ? ;number of the disk with the
;start of the central directory 2 bytes
zDskNrEntry dw ? ;total number of entries in
;the central dir on this disk 2 bytes
zDirNrEntry dw ? ;total number of entries in
;the central dir 2 bytes
zDirSiz dw ?,? ;size of the central directory 4 bytes
zDirOfs dw ?,? ;offset of start of central
;directory with respect to
;the starting disk number 4 bytes
zCmtLen dw ? ;zipfile comment length 2 bytes
zCmt db ? ;zipfile comment (variable size)
zdirEnd ENDS
CSEG segment para public
assume CS:CSEG,DS:CSEG
org 5CH ;FCB #1
db ? ;drive val
fcb1 db 10H dup(?) ;5DH, FCB #1 first char
fcb2 db 10H dup(?) ;6DH, FCB #2 first char
org 80H
nchar db ?
params db ?
org 9EH ; We'll use the default PSP DTA,
PSP_DTA_Name db ? ; and the file name found will be HERE.
;program entry point
org 100H
Zdir proc near
jmp Start ;skip over runtime data
usage db CR,LF,9,9, 'ZDIR version 1.8, 920124',CR,LF
db 9,9,'David Kirschbaum, Toad Hall/mod GWS',CR,LF
db 9,9, 'USAGE: ',9,'ZDIR zipname[.zip] [afn] [-v|-m]',CR,LF
db 9,9,9,'zipname may be ambiguous (wildcarded)',CR,LF
db 9,9,9,'afn = ambiguous member file name (like a*b)', CR,LF
db 9,9,9,'-v = verbose display',CR,LF
db 9,9,9,'-m = monosyllabic display',CR,LF
USAGELEN equ $ - usage
ziptyp db '.ZIP'
ZIPTYPLEN equ $ - ziptyp
msg1 db CR,LF, 9, 9, 9, ' ZIP file: '
MSG1LEN equ $ - msg1
msg2 db 'ZIP file not found',CR,LF
MSG2LEN equ $ - msg2
msg3 db 'Central directory not found',CR,LF
MSG3LEN equ $ - msg3
msg4 db 'ZIP is out of alignment or it''s not a ZIP'
crlf db CR,LF
MSG4LEN equ $ - msg4
CRLFLEN equ $ - crlf
;Keith Petersen suggested this oughtta go .. Sigh ...
;rivvvt db 'Rivvvvt',CR,LF
;RVTLEN equ $ - rivvvt
handle dw 0
flag1 db LOW(TRUE) ;Find First flag
verbose db PLAIN ;verbose display switch
;PLAIN, VERBO, or MONOSYLLABIC
znameptr dw 0 ;point past ZIP target file path
mnameptr dw 0 ;remember .zFilename start
dirNrEntry dw 0 ;central directory file count
dirctr dw 0 ;for counting down members
pathflag db ' ' ;is set to '+' if member
; filename includes a path
vhdrflag db FALSE ;set true when first member
;file is found
Comment ~ Looks like:
+filename.typ 000K / 000K +filename.typ 000K / 000K +filename.typ 000K / 000K
Comment ends ~
blankline db ' . K / K . K / K '
db ' . K / K',CR,LF
LINELEN equ $ - blankline
; Verbose display data
; display lines for verbose
; Adding a "E" (just before Stowage style) if encrypted
vhdr db CR,LF
db ' Name Length E Stowage SF Size now Date Time CRC'
db CR,LF
db ' ============ ======== = ======== ==== ======== ========= ====== ========'
db CR,LF
VHDRLEN equ $ - vhdr
vline label byte ;db CR,LF
vname db 15 dup (' ')
vlength db 9 dup (' ') ; length in archive
vencflag db 3 dup (' ') ;"E" if encrypted, else blank
vstyle db 10 dup (' ') ; compression method (text)
vfactor db ' xx% ' ; compression factor (percentage)
vsize db 10 dup (' ') ; actual file bytes
vdate db 'dd ' ; creation date
vmonth db 'mmm '
vyear db 'yy '
vtime db 'hh:mm ' ; creation time
vcrc db 'xxxxxxxx' ; 32-bit crc in hex
db CR,LF
VLINELEN equ $ - vline
; final totals line
vthdr db '*Total '
vtmbrs db 5 dup (' ')
vtlen db 8 dup (' '),' '
db 12 dup (' ')
vtsf db ' % '
vtsize db 8 dup (' ')
db CR,LF ; for tom
VTHDRLEN equ $ - vthdr
;Totals for each ZIP file's members:
totcmp dw 0,0 ; total of file lengths
totuncmp dw 0,0 ; total of file sizes
totmbrs dw 0 ; total number of files
TOTLEN equ $ - totcmp
;Totals for ALL ZIP files displayed:
ttotcmp dw 0,0 ;total compressed file size
ttotuncmp dw 0,0 ;total uncompressed file size
ttotmbrs dw 0 ;total nr member files
; ZIP compression types:
;#define STORED 0 /* compression methods */
;#define SHRUNK 1
;#define REDUCED1 2
;#define REDUCED2 3
;#define REDUCED3 4
;#define REDUCED4 5
;#define IMPLODED 6
;#define TOKENIZED 7
;#define DEFLATED 8
;#define NUM_METHODS 9 /* index of last method + 1 */
MAXSTYLES EQU 8 ;v1.8
zstyles label byte
db ' Stored' ;0 - The file is stored (no compression)
db ' Shrunk' ;1 - The file is Shrunk
db 'Reduced1' ;2 - Reduced with compression factor 1
db 'Reduced2' ;3 - Reduced with " " 2
db 'Reduced3' ;4 - Reduced with " " 3
db 'Reduced4' ;5 - Reduced with " " 4
db 'Imploded' ;6 - The file is imploded
db 'Tokenize' ;7 - The file is "tokenized" v1.8
db 'Deflated' ;8 - The file is Deflated v1.8
db ' Unknown' ;illegal or unknown value
months db 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec '
; translation table for upper-casing national chars:
UpperSet Db 'ÄÖÜÇÅÉÆÑ' ; upper case national chars
LowerSet Db 'äöüçåéæñ' ; lower case national chars
Zdir endp
Start proc near
call Parse_CmdLine ;parse cmdline for
;target filenames
jb Jmp_Msg_Term ;failed
;Let's get to work
call Find_Zip
jnc ZipLup_73 ;found the first one
mov dx,offset msg2 ;'Zipfile not found'
mov cx,MSG2LEN
Jmp_Msg_Term:
jmp Msg_Term ;display, terminate
ZipLup_73:
call Read_CentralDir ;try to read in file trailer
jnb Got_Dir ;found it, DS:BX -> directory
mov dx,offset msg3 ;'No central directory'
mov cx,MSG3LEN
jmp short Next1 ;right to next ZIP file
Got_Dir:
; We'll only show our verbose header line
; when and if we find our first member file.
; (Down in Show_FileData)
cmp verbose,MONOSYL ;mode switch
ja MemberLup ;verbose, no init
jb GD_InitLine ;plain, use standard
mov di,offset blankline ;if monosyllabic, blank
mov al,' ' ; that blankline for
mov cx,LINELEN-2 ; good
rep stosb
GD_InitLine:
call Refresh_LineBuff ;Init formatted display line
MemberLup:
cmp word ptr [bx],4B50H ;signature?
jnz Bad_Dir ;nope, bogus
cmp word ptr 2[bx],0201H ;normal entry?
jnz Bad_Dir ;nope, bogus
;Normal directory entry. Display it.
call Show_FileData ;display entry info
dec dirctr ;decr nr entries
jz Next_Zip ;last entry
jmp MemberLup ;next member
Bad_Dir:
mov dx,offset msg4 ;'ZIP is out of alignment'
mov cx,MSG4LEN ; or not a .ZIP file'
call Pr_StdOut ;display msg
Next_Zip:
mov dx,offset crlf ;need a new line
mov cx,CRLFLEN ;length
cmp verbose,VERBO ;verbose mode?
jz Show_Verbose_Totals ;yep, maybe display totals
mov ax,offset linebuff ;formatted line start
cmp di,ax ;got any file entries?
jz Next1 ;nope
dec di ;back up over the two spaces
dec di ; from the last file size
mov dx,ax ;DX'll need it for display
mov cx,ax ;CX will be nr chars
mov ax,0A0DH ;CR/LF
stosw ;stuff
stosw ;2 CR/LFs
xchg cx,di ;CX = end, DI = start
sub cx,di ;end - start = length
jmp short Next1
;Verbose mode
Show_Verbose_Totals:
cmp totmbrs,0 ;any totals?
jz Next1 ;nope
call Format_Totals ;yep, format
;CX,DX prepared for ...
; If just 1 member, there won't BE a total display!
; BP returns from Format_Totals with 0 (1 file) or 1 (more than
; 1 file)
or bp,bp ;just 1 file?
jz Next2 ;yep, no totals
Next1:
call Pr_StdOut ;display line seg or CR/LF
Next2:
call Find_Zip ;next .ZIP file
jnc ZipLup_73 ;found it, loop
xor ax,ax ;handy 0/FALSE
cmp al,verbose ;nonverbose mode?
jle NoMsg_Term ;nonverbose, terminate
;(errorlevel 0)
; We've been accumulating overall totals.
; Now display them. Gotta move the total totals
; into the totals (where Format_Totals expects them).
; If we never displayed our verbose header line,
; we never found a qualifying member file!
; Ergo .. no totals ..
cmp vhdrflag,al ;FALSE ;no header line?
jz NoMsg_Term ;no hdr, so no totals
mov ax,ttotmbrs ;accumulated total mbrs
cmp ax,dirNrEntry ;just the one ZIP?
jz No_More ;yep, forget total totals
cmp ax,1 ;<= 1?
jbe No_More ;yep, forget the total totals
mov si,offset ttotcmp ;move total totals
mov di,offset totcmp ;into totals
mov cx,TOTLEN SHR 1 ;nr words
rep movsw
call Pr_CrLf ;down extra line
call Format_Totals ;format overall totals
call Pr_StdOut ;display them
No_More:
; Keith Petersen suggested this oughtta go .. Sigh ..
; mov dx,offset rivvvt
; mov cx,RVTLEN ;fall thru to...
xor ax,ax ;ERRORLEVEL 0
jmp short NoMsg_Term
Msg_Term:
push ax ;save errorlevel
call Pr_StdOut ;display error msg
pop ax
NoMsg_Term:
mov ah,4Ch ;terminate, AL = ERRORLEVEL
int 21h
Start endp
;SUBROUTINE
Find_Zip proc near
;We reset our DTA to the default PSP DTA each time through.
;The actual ZIP file open uses another one.
mov dx,80H ;use PSP DTA
mov ah,1AH ;set DTA
int 21h
;v1.7a new code
mov ah,4FH ;assume not first time thru
;think how many bytes,
; machine cycles THIS saved!
cmp flag1,0FFh ;first time thru?
jne Find_Done ;nope
;First time through
not flag1 ;set to NOT first time thru
mov dx,offset ziptarget ;DS:DX -> zip target name
xor cx,cx ;read-only
dec ah ;4EH ;find first
Find_Done:
int 21h ;hah - two bytes saved!
jb FindZ_X ;failed, return CF set
;v1.7a new code ends
Comment ~ introduced in v1.7
Once DOS has found a file, we could apply our own matching
on top (DOS will maybe find MORE than we like, but never LESS).
Basically, that's another 'call MatchW'; remember to 'CLC' before
returning, so that we will be called again. - Just one problem:
We need our old name, as used to be in ziptarget; but that has
now been overwritten with the previous matching file name. So
we'd need to keep the old name somewhere... Too lazy to do it now.
End of comment v1.7 ~
call Move_FileName ;move name in after path
;to create full name for open
;Found target file. Announce, set new DTA, open it.
mov dx,offset msg1 ;'Zip file: '
mov cx,MSG1LEN
call Pr_StdOut
Print ziptarget ;'filename.zip'
call Pr_CrLf ;new line
;We need a new DTA for the file open/read so we don't blow away
;the find first/find next stuff in the PSP DTA.
mov dx,offset dta1BA ;DS:DX -> new DTA
mov ah,1Ah ;set DTA
int 21h
mov dx,offset ziptarget ;DS:dx -> filename buffer
mov ax,3D00H ;open file, read only
int 21h
mov handle,ax ;save handle
FindZ_X:
ret
Find_Zip endp
Move_FileName proc near
mov si,offset PSP_DTA_Name ;FCB #1 +1
mov di,znameptr ;pointer to after path
mov cx,6 ;first 12 bytes
rep movsw
movsb ;13th byte
ret
Move_FileName endp
;Reads in file tailer.
;Then scans for the unique central directory signature.
;On success:
; DS:BX -> central directory structure
; readlen = nr bytes actually read
; CF clear
;Else CF set for failure
Read_CentralDir proc near
xor cx,cx ;CX:DX = offset from end
xor dx,dx ;to very end
mov bx,handle ;file handle
mov ax,4202H ;move file pointer to end
int 21H ;gets file size in DX:AX
jb RCD_Close ;seek failed
; Increased ENDOFS to read in more of the ZIP file's tail.
; Original value wasn't enough to allow for huge comments.
sub ax,ENDOFS ;back up psn.lo
jnb Read1 ;ok, no problem
sub dx,1 ;got a borrow, decr psn.hi
jnb Read1 ;no problem
xor ax,ax ;sigh .. small file ..
xor dx,dx ; .. back to very start
Read1:
mov cx,dx ;psn.hi
mov dx,ax ;psn.lo
mov ax,4200H ;now move from start
int 21H ;CX:DX point to offset from end
;(big) or to start (small)
jb RCD_Close ;failed, close up
mov cx,ENDOFS ;try to read this much
mov dx,offset dirbuff ;into our directory buffer
mov di,dx ;DI'll need it in a second
mov ah,3FH ;read from file/device
int 21H ;AX=bytes read
jb RCD_Close ;failed, close up
;First scan the end structure to locate the central directory.
call Sig_Scan ;find the structure start
;(using bytes read in AX)
jb RCD_InvalidDir ;failed, close and exit
;ES:DI -> end structure start (the signature)
;While we have the end structure, let's pick up and display
;any ZIP file comment.
mov cx,[di].zCmtLen ;comment length
jcxz RCD_NoComment ;nope, forget it
lea dx,[di].zCmt ;DS:DX -> comment
call Pr_StdOut ;display it.
call Pr_CrLf ;and a new line
RCD_NoComment:
; Pick up the central directory file pointer (long integer):
; It's a pointer directly to the central directory start.
; This will only work for single-disk ZIP files.
mov si,[di].zDirSiz ;save central directory size
mov ax,[di].zDirNrEntry ;nr central dir entries
mov dirNrEntry,ax ;save it for later
mov dirctr,ax ;two places
mov dx,[di].zDirOfs ;central directory offset.lo
mov cx,[di].zDirOfs[2] ;offset.hi
mov ax,4200H ;move file ptrs from start
int 21H
jb RCD_Close ;seek failed, close and exit
mov cx,si ;read central dir size bytes
mov dx,offset dirbuff ;into our directory buffer
mov ah,3FH ;read from file/device
int 21H
jb RCD_Close ;failed, forget it
mov di,dx ;DI -> dir start
xor ax,ax ;return AX=0 for ok
cmp word ptr [di],4B50H ;is it a signature?
jnz RCD_InvalidDir ;nope
cmp word ptr 2[di],0201H ;is it a file entry sig?
jz RCD_Close ;yep, good to go, CF clear
RCD_InvalidDir:
mov al,11 ;Invalid format
stc ;insure CF set, AL=error
RCD_Close:
mov dx,ax ;save error value (if any)
pushf ;save flags
mov ah,3EH ;close file
int 21H ;(BX = file handle)
popf ;restore orig flags
mov ax,dx ;and any error value
mov bx,di ;if it went well,
;DS:BX -> central dir struc
ret
; Subroutine for Read_CentralDir
; Scans backwards through buffer for a directory end signature.
; Enter with:
; SI = second two signature bytes
; DX -> buffer start
; AX = bytes read
;We must scan at the byte level (since a member or directory entry
;can be ANY length).
Sig_Scan:
mov di,dx ;ES:DI -> dirbuff
;(now a ZIP end dir structure)
add di,ax ;+ bytes read = -> buff end
mov cx,ax ;bytes read for the scan
inc cx ;debug for MAX size
inc cx
mov ax,4B50H ;scan for first signature char
;(same for all structures)
mov si,0605H ;2d 2 chars of a directory
;end structure signature
std ;scan from end to start
SS_Lup:
repne scasb ;ES:DI -> read buffer
jnz No_Sig ;not found
jcxz No_Sig ;scanned it all
;ES:DI -> the byte BEFORE the 50H signature
cmp 2[di],ah ;2d signature byte?
jnz SS_Lup ;nope, keep searching
cmp 3[di],si ;last 2 signature bytes?
jnz SS_Lup ;nope, keep searching
inc di ;bump from that last scasb
;DS:DI -> dir structure
cld ;forwards again to be neat
clc ;CF clear for success
ret
No_Sig: cld ;forward again to be neat
stc ;return CF set for failure
ret
Read_CentralDir endp
;SUBROUTINE
;1 call
Show_FileData proc near
;Added tests for cmdline member name testing
call Member_Test ;see if file is eligible
jnz SF_NextRec ;nope, forget it
push bx
xor ax,ax ;FALSE
cmp al,verbose ;not verbose?
jle SFD_NonVerbose ;not verbose
;Verbose mode
cmp vhdrflag,al ;FALSE ;verbose hdr displayed?
jnz SFD_HdrDone ;yep
not vhdrflag ;set to TRUE
mov dx,offset vhdr ;display verbose header
mov cx,VHDRLEN
call Pr_StdOut
SFD_HdrDone:
call Show_Verbose ;show verbose member display
jmp short SFD_BumpPtrs ;skip nonverbose stuff
;Nonverbose mode
SFD_NonVerbose:
call Stuff_FileName ;parse, pad at ES:DI
cmp verbose,MONOSYL ;monosyllabic output?
je SFD_FlushLine ;shove it out
lea si,[bx].zCmpSiz ;compressed size (long int)
call Stuff_FileSize ;to ES:DI
lea si,[bx].zUncmpSiz ;uncompressed size
inc di ;adjust line ptr
call Stuff_FileSize ;stuff uncompressed size
; to ES:DI
cmp di,offset linebuff + LINELEN ;hit end?
jb SFD_BumpPtrs ;nope
SFD_FlushLine:
mov dx,offset linebuff ;display the line
mov cx,LINELEN ;length
call Pr_StdOut ;display it
call Refresh_LineBuff ;refresh dynamic variable
SFD_BumpPtrs:
pop bx
SF_NextRec:
;Now bump our BX pointer to the next entry
lea ax,[bx].zFilename ;from filename start
add ax,[bx].zNameLen ;add in name field length
add ax,[bx].zExtraLen ;and extra field length
add ax,[bx].zFilCmtLen ;and file comment field length
mov bx,ax ;should be next record
ret
Show_FileData endp
;Tests to see if THIS file is eligible (e.g., meets the ambiguous
;filename entered on cmdline at startup).
; If no such parm, accept it (return CF clear).
; If it matches, return CF clear.
; If no match, return CF set.
;While we're here, let's Asciify that stupid ZIP directory file name
;for later tests.
Member_Test proc near
mov cx,[bx].zNameLen ;name length
jcxz MT_Fail1 ;zero .. flunk it!
push di ;save formatted line pointer
lea si,[bx].zFilename ;dirEntry filename
mov di,si ;start
dec di ;back it up one
mov ax,di ;remember new start
mov dx,cx ;save length
rep movsb ;move it left 1 char
;(make room for AsciiZ 0)
mov byte ptr [di],0 ;AsciiZ the name
mov cx,dx ;restore count/length
mov si,ax ;new start (1 char to left)
mov dx,ax ;remember in DX
mov mnameptr,ax ;assume no paths
cmp byte ptr 1[si],':' ;a drive separator?
jnz MT_NoDrive ;nope
mov ax,2
add si,ax ;bump past d:
sub cx,ax ;adjust length counter
jbe MT_Fail ;zeroed out, flunk it!
MT_NoDrive:
;Check for directory slashes
mov di,si ;starting point
;PKZIP uses the '/' character for paths!
; Wonder why GWS is loading the entire AX?
; We're only doing a SCASB for the slash!
; Aha! Because of that je MT01 below .. tricky, tricky...
mov ax,' /' ;scan for PKZIP
; directory slashes
MT_SlashScan:
repne scasb
jnz MT_NoSlash ;none
jcxz MT_Fail ;zeroed out, flunk it!
mov si,di ;new starting point
mov byte ptr [di-1], '\' ;old habits never die
jmp MT_SlashScan ;and try again until all gone
MT_NoSlash:
;SI now points at first filename char.
cmp verbose,MONOSYL ;are we monosyllabic?
je MT_01 ;yes,remember full path
mov mnameptr,si ;remember the new address
; We'll set a global flag if there was a path.
; Simpler than testing if mnameptr = filename start
; in both verbose and nonverbose display modes.
; DX -> shifted .zFilename
; SI -> filename first char
mov ax,'+ ' ;assume yes (no paths)
;(AL=' ',AH='+')
cmp dx,si ;name^ = zFileName^?
jz MT_1 ;yep, no path
MT_01: ;if monosyl, use ' '!
mov al,ah ;'+' ;set the path flag
MT_1:
mov pathflag,al ;post path flag
cmp byte ptr pname1,0 ;empty member name?
jz MT_Done ;empty, forget the compares
mov di, si
mov si, offset pname1
call MatchW
pop di
ret
MT_Fail: ;no match
pop di ;restore formatted line ptr
MT_Fail1:
mov al,1 ;return ZF clear
MT_X: ret
MT_Done:
pop di
xor al,al ;return ZF set for success
ret
Member_Test endp
; The following routine introduced in v1.7:
MatchW Proc Near
; This function checks if a given string matches a given pattern
; including generalized wildcards, e.g., 'a*b?c'. The matching is
; case-sensitive. The result is returned in the flags:
; ZF set = exact match (check with JZ ExactMatch)
; CF set = no match ( JB NoMatch)
; CF clear, ZF clear = prefix match ( JA PartialMatch)
; (or JAE PartialOrExact)
;
; Hand-assembly from a Fortran routine by Gunter Rademacher
;
; Input
; si - pointer to pattern, ASCIIZ
; di - pointer to string, ASCIIZ
; Output
; carry and zero flags (cf. above)
; Registers modified
; ax, cx, si, di
; Let's use BX and DX (saving them on the stack just in case
; they need to be preserved) instead of the variables saveSI and saveDI.
; Gain speed, reduce size.
push bx ;preserve
push dx
mov dx,di ;save DI a sec
xor ax,ax ;clear lsb and msb
;(to find end of AsciiZ
; string, later as flag)
; Bad assumption here: What is CX prior to this REPNE SCASB?
; Donno .. so we'd better make sure it's big, ne?
; I can live with assuming we're already CLD'ed (forward)
; I also don't see where saveSI was ever initialized!
; I can only assume pattern start (since I don't quite have the
; logic of this sucker figured out yet!).
mov bx,si ;initialize saveSI
mov cx,0FFFFH ;max number
Repne Scasb
Dec di
Dec di
xchg di,dx ;DI restored (first char)
;DX=pointer to last char
; AH is already 0 (possible return code)
Xor cx, cx ; wildcard pointer
GetPat: Lodsb ; get char from pattern
Or al, al
Jz ChkMore ; branch if end of pattern
Cmp al, STRINGWILD ;'*'
Je SaveStar ; branch if string wildcard
Cmp Byte Ptr [di], 0
Je MatchWr ; branch if at end of string
Scasb ; branch if pattern char matches
Je GetPat ; string wildcard
Cmp Al, CHARWILD ;'?'
Je GetPat ; ... or if char wildcard
MatchAny: Dec cx
inc dx ; restore last psn for
mov di,dx ; string wildcard + 1
Cmp Byte Ptr [di], 0
Je MatchWr
mov si,bx ; restore corresponding
; pattern
Jmp Short GetPat ; ... position and loop
SaveStar: Cmp Byte Ptr [si], 0
Jz Found ; branch if end of pattern
Mov cx, 1 ; mark this fact
mov dx,di ;save pointer to string
mov bx,si ;and pointer to pattern
Jmp Short GetPat ; and loop
ChkMore: Mov ah, 2 ; at least substring match
Cmp Byte Ptr [di], 0
Jne MatchAny ; loop if not at end of string
Found: Mov ah, 1 ; true match!
MatchWr: Sub ah, 1 ; done; set up flags
pop dx ;restore
pop bx
Ret
MatchW EndP
; End of routine introduced in
;ES:DI -> next position on formatted line
;zFilename is now AsciiZed
;mnameptr contains ptr to original or real zFilename start
;(paths stripped)
Stuff_FileName proc near
push di ;save formatted line ptr
; Now using pathflag if target filename has a path
mov al,pathflag ;' ' if no paths,
;'+' if paths
mov si,mnameptr ;ptr to filename start
stosb ;stuff space or '+'
mov dx,di ;name start
add dx,8 ;bump to the dot
SN_Lup: lodsb
or al,al ;hit AsciiZ 0 yet?
jz SN_Done ;yep
cmp verbose,MONOSYL ;monosyllabic?
je SN_NoDot ;ay,don't mess with '.'
cmp al,'.' ;.typ separator?
jnz SN_NoDot ;nope
cmp di,dx ;where the dot should go?
jz SN_NoDot ;yep, put it there
mov di,dx ; bump to the dot psn
SN_NoDot:
stosb ;stuff in formatted line
jmp SN_Lup
SN_Done:
pop di ;orig ptr
add di,14 ;bump to size psn
ret
Stuff_FileName endp
;SUBROUTINE
;2 calls
Stuff_FileSize proc near
push bx ;Preserve BX! (dta ptr)
add di,3 ;move to number string end
push di ;save it (ptr to last digit)
mov ax,[si]
mov dx,[si+2]
add ax,3FFh ;div 1024 to get Kb
adc dx,0
mov cl,0Ah
shr ax,cl
mov cl,6
shl dx,cl
add ax,dx
mov si,0Ah
SFS_Lup:
xor dx,dx
div si
add dl,30H ;asciify
dec di ;back up the digit pointer
mov [di],dl ;stuff digit in buffer
or ax,ax ;number done?
jnz SFS_Lup ;nope
pop di ;restore line buffer ptr
add di,3 ;bump past ' / ' or ' | '
pop bx
ret
Stuff_FileSize endp
;Verbose display functions
;Format, display single line for each member
;On success, return:
; CF clear
; AL = 0
;On error, return:
; CF set (because of output write fail)
; AL = error code
;Preserve BX (buffer ptr)
; Right before the compression style:
; "E" if encrypted
; Else blank
; Forgot to show our possible "extended" filename with path
; (via a "+" preceding the stripped filename).
; Worked fine for nonverbose mode, now adding for verbose mode.
sign db ' ' ;local variable
hundred dw 100 ; for computing percentages
Show_Verbose proc near
mov si,mnameptr ;move real member name
;(no paths)
mov di,offset vname ;into formatted line
mov al,pathflag ;' ' if no paths,
;'+' if paths
stosb ;stuff '+'
mov cx,13 ;14 spaces in field
; minus the path flag
; already stuffed
SV_Lup:
lodsb ;snarf char
or al,al ;AsciiZ ending?
jz SV_5 ;yep
stosb
loop SV_Lup
SV_5:
; I don't see where we EVER loaded AH with a space!
; Must have lost it in one of the hacks.
; Replacing it again.
mov al,20H
rep stosb ;pad with spaces
; reduce the size/length to word values
mov si,[bx].zUncmpSiz ; get uncompressed file size
mov ax,[bx].zUncmpSiz[2]
mov cx,[bx].zCmpSiz ;compressed size
mov dx,[bx].zCmpSiz[2]
SVL_51: or ax,ax ; big number?
jz SV_52 ; nope, can use it
shr ax,1 ; yup, divide by two
rcr si,1
shr dx,1
rcr cx,1
jmp SVL_51 ;loop
SV_52:
mov ax,si ; low word of actual size
mov sign,' '
cmp ax,cx ; arc member is larger?
jb SV_520
sub ax,cx ; amount saved
jmp short SV_521
SV_520:
sub ax,cx
neg ax
mov sign,'-'
SV_521:
mul hundred ; to percentage
add ax,50
; I'm thinking PK isn't doing this rounding ..
; our percentage figures are sometimes 1% off the PKZIP -v display.
; Close enough for govt work...
adc dx,0 ; round up percent
or si,si ; empty file?
jnz SV_53
mov ax,100
jmp short SV_54
SV_53: div si
SV_54: cmp ax,100 ; archive fouled?
jbe SV_55
sub ax,ax
SV_55:
mov di,offset vfactor-2 ;format stowage factor
call Asciify ;display AX
mov al,sign
mov vfactor,al
; If encrypted, stuff an 'E', else a blank
mov di,offset vencflag ;space for "E", space
mov ax,' E' ;assume unencrypted
;(AL='E',AH=' ')
test byte ptr [bx].zBitFlag,1 ;If 0 bit is set,
jnz SV_56 ;it's encrypted
mov al,ah ;' ' ;blank (not encrypted)
SV_56:
stosw ;stuff 'E' or space,
;trailing space
;DI -> vstyle field
; Adding test to insure compression method (0..6) is in legal range
; (e.g., doesn't overrun our compression style table)
mov si,offset zstyles ;style table start
xor ax,ax ;clear msb
or ax,[bx].zCmpMeth ;bring in method
; of compression
jz SV_58 ;0 -> use table base
;v1.8 cmp al,6 ;max legal
cmp al,MAXSTYLES ;max legal v1.8
jbe SV_57 ;it's legal
;v1.8 mov al,7 ;' Unknown'
mov al,MAXSTYLES+1 ;' Unknown' v1.8
SV_57:
mov cl,3 ; eight bytes each entry
shl ax,cl
SV_58:
add si,ax ;table base + offset
;DI already points to vstyle
mov cx,4 ;move as words (8 bytes)
rep movsw
mov dx,[bx].zCmpSiz[2] ;compressed size.hi
mov ax,[bx].zCmpSiz ;compressed size
add totuncmp,ax ;accumulate
adc totuncmp[2],dx
mov di,offset vsize ;format file size
call Asciify_Long
mov dx,[bx].zUncmpSiz[2] ;uncompressed size.hi
mov ax,[bx].zUncmpSiz ;uncompressed size.lo
add totcmp,ax ;accumulate
adc totcmp[2],dx
mov di,offset vlength ;format file length
call Asciify_Long
mov ax,[bx].zModDate ; format file date
call GetDate
mov ax,[bx].zModTime ; format file time
call GetTime
mov ax,[bx].zCrc32 ; format crc.lo in hex
mov di,offset vcrc + 4
call Cvh
mov ax,[bx].zCrc32[2] ; format crc.hi in hex
mov di,offset vcrc
call Cvh
inc totmbrs ;bump total file count
mov dx,offset vline ;display formatted info
mov cx,VLINELEN
call Pr_StdOut
ret
Show_Verbose endp
;Formats, displays verbose totals
Format_Totals proc near
mov ax,totmbrs ;total members
add ttotmbrs,ax ;accumulate
; Don't display totals for this ZIP file's members
; unless there's more than 1.
xor bp,bp ;use BP for a flag
;(undisturbed by Asciify)
;(0 = only 1 file)
cmp ax,1 ;just one?
jbe FT_1 ;yep, no Asciify
inc bp ;flag more than 1 file
mov di,offset vtmbrs-2 ;format total members
call Asciify
FT_1: mov dx,totcmp[2] ;total compressed file size
mov ax,totcmp
add ttotcmp,ax ;accumulate total totals
adc ttotcmp[2],dx
or bp,bp ;just 1 file?
jz FT_2 ;yep, no Asciify
mov di,offset vtlen ;format total compressed file
;size
call Asciify_Long
FT_2:
mov dx,totuncmp[2] ; total uncompressed file size
mov ax,totuncmp
add ttotuncmp,ax ;accumulate total totals
adc ttotuncmp[2],dx
or bp,bp ;just 1 file?
jz FT_9 ;yep, no Asciify
;no fancy computations
mov di,offset vtsize ;format total uncompressed
;file size
call Asciify_Long
; reduce the total size/length to word values
mov si,totcmp ; get compressed file size
mov ax,totcmp[2]
mov cx,totuncmp ; uncompressed file size
mov dx,totuncmp[2]
FTDiv: or ax,ax ; big number?
jz FT_4 ; nope, can use it
shr ax,1 ; yup, divide by two
rcr si,1
shr dx,1
rcr cx,1
jmp short FTDiv
FT_4:
mov ax,si
mov sign,' ' ;whata kludge
cmp ax,cx ;compressed > uncompressed?
jb FT_5 ;yep
sub ax,cx ;amount saved
jmp short FT_6
FT_5: sub ax,cx
neg ax
mov sign,'-'
FT_6: mul hundred ; to percentage
add ax,50
adc dx,0 ; round up percent
or si,si ; empty file?
jnz FT_7
mov ax,100
jmp short FT_8
FT_7: div si
FT_8: mov di,offset vtsf-2 ;format stowage factor
call Asciify ;AX
mov al,sign
mov vtsf,al
FT_9:
mov di,offset totcmp ;starting at totcmp
mov cx,TOTLEN/2 ;length of totals to clear
; (words)
xor ax,ax ;handy 0
rep stosw
or bp,bp ;just 1 file?
jz FT_X ;yep, exit
mov dx,offset vthdr ;prepare to display totals
mov cx,VTHDRLEN ;msg length
FT_X:
ret ;to display
Format_Totals endp
; format the time (in AX)
time record hour:5,min:6,sec:5 ;packed time
GetTime proc near ;format the date
mov di,offset vtime
or ax,ax ;it is zero?
jz GotTime
push ax ;save date
and ax,mask hour ;get hour part
mov cl,hour ;bits to shift
shr ax,cl
call Cnvrt1
stosw
mov al,':'
stosb
GT3: pop ax ;get the time back
and ax,mask min ;get min part
mov cl,min ;bits to shift
call Cnvrt
stosw
GotTime:ret
GetTime endp
Cnvrt2 proc near ;convert to ascii
call Cnvrt
cmp al,'0' ;suppress leading zero
jne Cnvrtd
mov al,' '
ret
Cnvrt: shr ax,cl
Cnvrt1: aam ;make al into bcd
or ax,'00' ; and to ascii
xchg al,ah
Cnvrtd: ret
Cnvrt2 endp
page
; format the date (in AX)
date record yr:7,mo:4,dy:5 ;packed date
GetDate proc near ;format the date
or ax,ax ;is it zero?
jz GotDate
push bx ;preserve BX (buff ptr)
push ax ;save date
and ax,mask yr ;get year part
mov cl,yr ;bits to shift
call Cnvrt
mov di,offset vyear
or al,'8' ;adjust for base year
stosw
pop bx ;get the date back
push bx ;save it
and bx,mask mo ;get month part
mov cl,mo ;bits to shift
shr bx,cl
add bx,bx ; form month table index
add bx,bx
lea si,word ptr months-4[bx]
mov di,offset vmonth
movsw ;2 bytes
movsb ;the 3rd
pop ax ;get the date back
and ax,mask dy ;get day part
mov cl,dy ;bits to shift
call Cnvrt
mov di,offset vdate
stosw
pop bx ;restore buff ptr
GotDate:ret
GetDate endp
;A severely hacked single/double precision number conversion function.
;Originally from JMODEM, but severely hacked by Toad Hall.
;ES:DI -> string
;Destroys everything almost.
;Enter here if integer in AX
Asciify proc near
push bx ;save buff ptr
xor dx,dx ; clear fake long.hi
mov si,ax ;move integer into SI
xor ah,ah ;clear msb (flag)
jmp short Ascii_Ax ;jump into the code
;Enter here if long integer in DX:AX.
Asciify_Long:
push bx ;save buff ptr
mov si,ax ;move long.lo into SI
xor ah,ah ;clear msb (flag)
Comment ~
Taking out the extremely high numbers to reduce column width.
MOV CX,3B9AH ; Get billions
MOV BX,0CA00H
CALL Subtr ; Subtract them out
MOV CX,05F5H ; Get hundred-millions
MOV BX,0E100H
CALL Subtr ; Subtract them out
Comment ends ~
and dx,4FFH ;seems likely
MOV CX,0098H ; Get ten-millions
MOV BX,9680H
CALL Subtr ; Subtract them out
MOV CX,000FH ; Get millions
MOV BX,4240H
CALL Subtr ; Subtract them out
MOV CX,1 ; Get hundred-thousands
MOV BX,86A0H
CALL Subtr ; Subtract them out
Ascii_Ax:
xor cx,cx ; Get ten-thousands
MOV BX,2710H
CALL Subtr ; Subtract them out
MOV BX,03E8H
CALL Subtr ; Subtract them out
MOV BX,0064H
CALL Subtr ; Subtract them out
MOV BX,10
CALL Subtr ; Subtract them out
mov ax,si ;residual in SI
add AL,'0' ; Add bias to residual
stosb ; Put in the string
pop bx ;restore buff ptr
RET
;Common subroutine for Asciify
Subtr: mov al,'0'-1
Subtr1: INC al ; Bump the digit character
SUB si,BX ; Dword subtraction
SBB DX,CX
JNB Subtr1 ; Continue until a carry
ADD si,BX ; One too many, add back
ADC DX,CX ; and the remainder
cmp al,'0'
jnz Subtr2 ;nope, turn off leading flag,
; stuff
or ah,ah ;no more leading spaces?
jnz Sub_Stuff ;right, stuff the '0'
mov al,' ' ;make it neat
; with leading spaces
Sub_Stuff:
stosb ;stuff the char
RET
Subtr2: inc ah ;turn off leading space flag
stosb
ret
Asciify ENDP
;Convert 16-bit binary word in AX
;to hex ASCII string at ES:DI
;(Protect BX, directory array pointer)
hexchar db '0123456789ABCDEF'
Cvh proc near
push bx ;save buff ptr
mov si,offset hexchar ;for faster access
mov dx,ax ; save 16-bits
mov bl,dh ; third nibble
xor bh,bh ;clear msb
mov cx,0F04H ;CL=4 for shifting,
;CH=0FH for masking
shr bl,cl
mov al,[si][bx] ;snarf hex char
stosb
mov bl,dh ; last nibble
and bl,ch ;0fh
mov al,[si][bx] ;snarf hex char
stosb
mov bl,dl ; first nibble
shr bl,cl ; isolate (CL still 4)
mov al,[si][bx] ;snarf hex char
stosb
mov bl,dl ; second nibble
and bl,ch ;0fh ; isolate
mov al,[si][bx] ;snarf hex char
stosb
pop bx ;restore buff ptr
ret
Cvh endp
;SUBROUTINE
Pr_CrLf proc near
mov dx,offset crlf
mov cx,CRLFLEN ;fall through to ...
Pr_CrLf endp
Pr_StdOut proc near
push bx ;preserve bx
mov bx,STDOUT
mov ah,40H ;write
int 21H
pop bx
ret
Pr_StdOut endp
;Print null-terminated (AsciiZ) string like int 21h function 9
;Enter with DS:DX -> AsciiZ string
;Destroys AX
;On success, return:
; CF clear
; AL = 0
;On failure (StdOut write fail), return:
; CF set
; AL = error
PrintS proc near
mov cx,0FFFFH ;max scan
xor al,al ;handy 0
mov di,dx ;string start
repne scasb ;find the terminator
inc cx ;adjust
not cx ;CX=length
call Pr_StdOut ;display to StdOut
ret
PrintS endp
;Reinit our nonverbose formatted display line
;Returne ES:DI -> formatted line start
Refresh_LineBuff proc near
mov si,offset blankline ;formatted display line
mov ax,offset linebuff ;dynamic data area
mov di,ax ;move to ...
mov cx,LINELEN ;length
rep movsb
mov di,ax ;ES:DI -> formatted line start
ret
Refresh_LineBuff endp
;Parses cmdline for target files.
;If failure:
; Returns CF set,
; AL = ERRORLEVEL,
; DX -> error msg
;Adding command line switch ('-v') parsing.
;And '-m', too. Also accept '/' as switch char.
;And fixing the bug.
Parse_CmdLine proc near
call _Args ;parse cmdline
;returns AX = argc
or ax,ax ;any argc?
jnz Got_Parm ;yep
Cmd_Err:
mov dx,offset usage ;intro, usage
mov cx,USAGELEN ;total length
mov al,1 ;errorlevel 1
stc ;CF set
ret ;for a jmp to Msg_Term
Got_Parm:
mov cx,ax ;argc
;See if any of the argv's are our '-v' verbose switch.
;Or '-m'. Or '/v'. Or '/m'. Or. Or. Or.
;If so, turn switch on, clear that argv.
cmp cx,1 ;just 1 arg?
jz Chk_ZipName ;yep, can't be any switches
mov bx,offset argv[2] ;start with ^argv(1)
Chk_VSwitch:
mov si,[bx] ;argv^(1)
cmp byte ptr 2[si],0 ;arg longer than 2 chars?
jnz Chk_VRelup ;yes, can't be switch
mov ax,[si] ;snarf possible -V parm
cmp al,'-' ;is first(!) char '-'?
je Found_Some_Switch ;yes, check which
cmp al,'/' ;is first(!) char '/'?
jne Chk_VRelup ;no, cant' be switch
Found_Some_Switch:
; We KNOW the args are upper case, because we made them so!
mov al,VERBO ;assume it's 'v'
cmp ah,'V' ;'V'?
jz Found_Switch ;yep
cmp ah,'M' ;'M'?
jnz Chk_VRelup ;nope
mov al,MONOSYL ;hah-it's monosyllabic
Found_Switch:
cmp byte ptr 2[si],0 ;Just the '-v'?
jnz Chk_VRelup ;nope, must be a name
mov verbose,al ;got switch,set verbose
mov byte ptr [si],0 ;clear this argv
cmp cx,argc ;first argc?
jnz Chk_V_NoShift ;nope
inc argc ; .. sigh .. readjust
call _Shift ;yep, move other args down one
;(will decr argc again)
Chk_V_NoShift:
dec argc ;final decr to eliminate it
jmp short Chk_ZipName ;done
Chk_VRelup:
add bx,2 ;next argv^
loop Chk_VSwitch ;check all args
;If argv(1) was the '-v' switch,
;or that other one,
;that argv has been cleared via the _Shift call.
;The target zip file name HAS to be argv(1)!
Chk_ZipName:
mov cx,argc ;argc arg counter
jcxz Cmd_Err ;he only had a switch!
mov bx,offset argv[2] ;^argv(1)
mov si,[bx] ;argv^(argc)
call Parse_MoveZipName ;handle the zip name
call _Shift ;move argv's down one
;Check for member testing (2d or 3d argv).
;We make the call even if argc = 0 (to clear a buffer)
Chk_MbrName:
mov di,offset pname1
mov byte ptr [di], 0 ;initialize mbr name
mov cx,argc ;argc counter
jcxz Parse_Done ;all done, no member
mov bx,offset argv[2] ;it's now ^argv(2)
mov si,[bx] ;argv^(argc)
Chk_MbrCopy: ;copy name to safe place
lodsb
stosb
or al, al ;at end?
jnz Chk_MbrCopy ;loop if not
Parse_Done:
clc
ret
Parse_CmdLine endp
;Cmdline parsing subroutine
;Moves argv into our targetfile buffer,
;picks up a pointer to past the paths (if any)
;SI -> argv
Parse_MoveZipName proc near
push si ;save SI
mov bx,offset ziptarget ;full zip target filename
mov di,bx
mov cx,64 ;clear ziptarget
xor ax,ax
rep stosw
mov di,bx ;DI -> ziptarget
Move_ZipName:
lodsb ;snarf cmdline char
or al,al ;AsciiZ terminator?
jz Skip_Zip_Path ;yep
stosb ;stuff
jmp Move_ZipName
Skip_Zip_Path:
;DI -> last ziptarget's real char +1
mov dx,di ;end, last char +1
sub dx,bx ;end - start = length
dec di ;adjust from last movsb
std ;backwards
mov si,di ;SI -> ziptarget last char
;BX = ziptarget start
;SI -> ziptarget lastchar
;DX = ziptarget length
;DI -> ziptarget lastchar
;Now find any path separators in ziptarget
mov cx,dx ;scan length
mov al,'\' ;subdir dividers
repne scasb ;scan till we hit one
jz Zip_GotPath ;DI -> char before the '\'
mov di,si ;back to last char
mov cx,dx ;scan length
mov al,':' ;how about just drive?
repne scasb
jnz Zip_NoPath ;nope, DI -> ziptarget-1
;skip the extra inc
Zip_GotPath:
inc di ;bump to '\' or ':'
Zip_NoPath:
inc di ;and past it to filename's char
cld ;forward again
mov znameptr,di ;points to start of
;true filename (beyond path)
;for later appending Find Next
;filenames to path
;append type of .ZIP, if not specified. Moved here from above in
mov dx,si ;end of file name
mov cx,dx
sub cx,di ;end - start = length
inc cx ;well, nearly...
mov al,'.'
repne scasb ;find a .TYP separator
;DI -> char after '.' or actual filename end
jz PZN_Exit ;leave .TYP alone!
mov si,offset ziptyp ;assume need full '.ZIP'
movsw ;copy .ZIP in
movsw ;(4 chars)
PZN_Exit:
pop si ;restore
ret
Parse_MoveZipName endp
Capitalize Proc Near
; This routine takes a pointer to an ASCIIZ string and uppercases the
; latter in place, taking national characters into account
; Input
; si - pointer to string
; Registers modified
; ax, cx, di, si
; National characters .. Gad! Sure do appreciate
; the European outlook .. we provincial New World types ...
Inc si
mov ah,20H ;handy constant
CNextChar: Lodsb ; get next char of string
Or al, al
Jz CExit ; branch if at end of string
Cmp al, 'a'
Jb CNextChar ; skip this char if below 'a'
Cmp al, 'z' ; if above 'z', then...
Ja CUpperHalf ; ...test for national chars
Sub al,ah ; 20h ; otherwise make upper case
Jmp Short CPutChar ; branch for replacement
CUpperHalf: mov cx, LowerSet-UpperSet ; number of national chars
mov di, offset LowerSet ; pointer to lower case letters
repne scasb ; try to find
jne CNextChar ; loop if not found
mov al, [di+UpperSet-LowerSet-1] ; otherwise replace
CPutChar: Mov byte ptr [si-1], al ; store back
Jmp Short CNextChar ; and loop
CExit: Ret
Capitalize EndP
;---- args.asm ----------------------------------------------------------
;from KEGELUNX.ARC Unix-like utils.
; Args parses the command line into a unix-style parameter array.
; CALL _ARGS to have it parse the cmdline.
; Argc and Argv are just as in C; argc = # of params on cmd line,
; argv is an array of pointers to strings (null terminated, of course).
; Because MS-DOS doesn't pass us the name used to invoke the program,
; argv[0] always points to a null string.
; _Shift updates argc.
;--------------------------------------------------------------------------
; Maximum number of parameters
MAXPARMS equ 4
_Args proc near
; initialize
cld ; clear decrement mode
mov bx, 2 ; argc = 0 (will sub 2 from bx at end)
mov argv[0], offset null ; prog name unknown; set to null.
mov si,offset params ; i = 0
mov cl,nchar ; cx = # of chars in command line
xor ch,ch
jcxz Args_Done ; no arg chars -> we're done.
mov di, si ; pointer to end of cmd line
add di, cx
mov [di+1],ch ; 0 ; ASCIIZ-ify command line
push si ; save vitals parms
push cx
call Capitalize ; convert to uppercase in situ
pop cx ; retrieve vital parms
pop si
mov di,offset argbuff ;big arg buffer
; Move arguments out of default DTA.
push cx
push di
rep movsb
pop si
pop cx
; Big loop- find arguments...
mov ah,20H ;handy space
ParmL:
; Little loop #1: strip leading blanks from argument.
; while (i<NCHAR && params[i] = ' ') do i++;
StripL: lodsb ; al = [si++]
cmp al,ah ; space?
loopz StripL ; if so, keep skipping.
jne Args_GotOne ; If we found a nonblank,
; skip zero test.
jcxz Args_Done ; found no unblank chars -> we're done.
Args_GotOne:
dec si ; bump SI back to start of nonblank.
inc cx
mov argv[bx],si ; save pointer to this string
; Little loop #2: skip nonblank chars.
; while (i<NCHAR && params[i] <> ' ') do i++;
SkipL: lodsb
cmp al,ah
loopnz SkipL
jz Oky
; Last char of line was not blank.
inc si ; make next statement put null
; AFTER arg.
Oky:
mov byte ptr [si][-1], 0 ; put null at end of arg
add bx,2 ; argc++
jcxz Args_Done ; if we ran off end of cmdline,
; no more args.
cmp bx, MAXPARMS*2
jb ParmL ; loop if argc < MAXPARMS.
Args_Done:
; All done finding parms; now share argc with caller.
mov ax,bx
sub ax, 2
shr ax, 1
mov argc,ax ;save argc
ret
_Args endp
;---- _Shift: --------------------------------------------
; Shifts %2 to %1, %3 to %2, etc. Leaves %0 alone.
; Works by shuffling argv[*].
_Shift proc near
cld
mov si, offset argv[4]
mov di, offset argv[2]
mov cx, MAXPARMS
rep movsw
dec argc
ret
_Shift endp
;---- parameter count, array ----------------
; Pointers to up to MAXPARMS parameter strings are held here.
null dw 0 ; the null string
argc dw ?
argv dw MAXPARMS dup (null)
;Dynamic variables start here.
;Not REAL big .. we could calculate them and release unneeded memory ..
;but who needs it? This ain't TSR, and the normal 64Kb required for
;a .COM program shouldn't stress anyone's system capacity.
dta1BA equ $ ;alternate DTA
ziptarget equ dta1BA + 42 ;up to 128 bytes
pname1 equ ziptarget + 128 ;11-byte FCB filename + 0
argbuff equ pname1 + 14 ;cmdline parsing buff
Comment ~ Looks like:
+filename.typ 000K / 000K +filename.typ 000K / 000K +filename.typ 000K / 000K
Comment ends ~
linebuff equ argbuff ;82 chars long
dirbuff equ linebuff + 82 ;ENDOFS bytes long
CSEG ENDS
END Zdir